/*
 * Copyright 2004 original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jbfilter.jsp.fcomps;

import java.util.Date;

import javax.servlet.jsp.JspException;

import org.jbfilter.core.EnableStrategy;
import org.jbfilter.core.FilterComponent;
import org.jbfilter.core.fcomps.multiple.StartEndDateFilterComponent;
import org.jbfilter.impl._JbFilterDateUtils;
import org.jbfilter.jsp.FilterTagCallable;
import org.jbfilter.jsp.JbfilterTag;
import org.jbfilter.jsp.JbfilterTagAttribute;
import org.jbfilter.jsp.TruncateUnitSupport;
import org.jbfilter.jsp.converters.ConverterException;
import org.jbfilter.jsp.converters.DateConverter;

public class StartEndDateFilterComponentTag<E>
	extends FilterComponentTag<E>
	implements FilterTagCallable {

	// the date fields accept dates or strings, for this reason they are Objects
	private Object startDate;
	private Object endDate;
	
	private boolean startDateSet = false;
	private boolean endDateSet = false;
	
	private Boolean endToLastMillisecondOfDay;
	
	private String varStartString;// if set : puts the start date (string value according to pattern) in the page
	private String varEndString;// same for end date

	private DateConverter dateConverter = new DateConverter();
	private TruncateUnitSupport truncateUnitSupport = new TruncateUnitSupport(JbfilterTag.startEndDateComponent);
	private String valueForNull = FilterComponentTag.BLANK;
	
	private Boolean includeBorders;
	private Boolean includeBorderStartDate;
	private Boolean includeBorderEndDate;
	
	@Override
	protected FilterComponent<E> createComponent() throws JspException {
		return getFilterComponentProvider().newStartEndDateFilterComponent();
	}
	@Override
	protected JbfilterTag getJbfilterTag() {
		return JbfilterTag.startEndDateComponent;
	}
	@Override
	protected void setPropertiesFilterComponentTagCallback() throws JspException {
		StartEndDateFilterComponent<E> comp = getStartEndDateFilterComponent();
		if (includeBorderStartDate != null) {
			comp.setIncludeBorderStartDate(includeBorderStartDate);
		}
		if (includeBorderEndDate != null) {
			comp.setIncludeBorderEndDate(includeBorderEndDate);
		}
		if (includeBorders != null) {
			comp.setIncludeBorders(includeBorders);
		}
		if (truncateUnitSupport.isTruncateSet()) {
			comp.setTruncate(truncateUnitSupport.getTruncate(valueForNull));
		}
		if (startDateSet) {
			startDate = prepareValue(startDate);
			comp.setStartDate(retrieveDate(startDate, JbfilterTagAttribute.startDate));
		}
		if (endDateSet) {
			endDate = prepareValue(endDate);
			comp.setEndDate(retrieveDate(endDate, JbfilterTagAttribute.endDate));
		}
	}
	private Object prepareValue(Object val) {
		return ValueForNullSupport.prepareValue(val, valueForNull);
	}
	@Override
	public void doTagCallback() throws JspException {
		StartEndDateFilterComponent<E> comp = getStartEndDateFilterComponent();
		pushDateAsStringAttribute(varStartString, comp.getStartDate());
		pushDateAsStringAttribute(varEndString, comp.getEndDate());
	}

	// push the date's string value in page scope
	private void pushDateAsStringAttribute(String varName, Date date) {
		if (varName != null && date != null) {
			setPageScopeAttribute(varName, dateConverter.toString(date));
		}
	}
	private Date retrieveDate(Object o, JbfilterTagAttribute att) throws JspException {
		if (o == null) {
			return null;
		}
		Date date = null;
		if (o instanceof Date) {
			date = (Date)o;
		} else if (o instanceof String) {
			try {
				date = dateConverter.fromString((String)o);
			} catch (ConverterException e) {
				// do nothing because this is wrong user input 
				return null;
			}

		} else {
			throw new JspException(
					new StringBuilder().
					append("Invalid type for attribute '").
					append(att).
					append("' in tag '").
					append(getJbfilterTag()).
					append("' : ").
					append(o.getClass().getName()).
					append(".").
					toString());
		}
		assert(date != null);
		// date should not be null here, the date != null test is only a garde-fou
		if (date != null && att == JbfilterTagAttribute.endDate && endToLastMillisecondOfDay == Boolean.TRUE) {
			date = _JbFilterDateUtils.getLastMillisecondOfDay(date);
		}
		return date;
	}	
	@Override
	protected EnableStrategy getJspEnableStrategy() {
		return new EnableStrategy() {
			@Override
			public boolean decideEnabled() {
				StartEndDateFilterComponent<E> fc = getStartEndDateFilterComponent();
				if (	fc.getStartDate() == null &&
						fc.getEndDate() == null &&
						!isNullTestingCase()){
						return false;
				} else {
					// at least one date is not null, or null testing is active
					return true;
				}
			}
		};
	}
	private StartEndDateFilterComponent<E> getStartEndDateFilterComponent() {
		return (StartEndDateFilterComponent<E>)getFilterComponent();
	}
	public void setPattern(String pattern) {
		dateConverter.setPattern(pattern);
	}
	public void setTruncate(String truncateAsString) {
		truncateUnitSupport.setTruncateAsString(truncateAsString);
	}
	public void setStartDate(Object startDate) {
		this.startDate = startDate;
		startDateSet = true;
	}
	public void setEndDate(Object endDate) {
		this.endDate = endDate;
		endDateSet = true;
	}
	public void setVarStartString(String varStartString) {
		this.varStartString = varStartString;
	}
	public void setVarEndString(String varEndString) {
		this.varEndString = varEndString;
	}
	public void setEndToLastMillisecondOfDay(Boolean endToLastMillisecondOfDay) {
		this.endToLastMillisecondOfDay = endToLastMillisecondOfDay;
	}
	public void setValueForNull(String valueForNull) {
		this.valueForNull = valueForNull;
	}
	public void setIncludeBorderStartDate(Boolean includeBorderStartDate) {
		this.includeBorderStartDate = includeBorderStartDate;
	}
	public void setIncludeBorderEndDate(Boolean includeBorderEndDate) {
		this.includeBorderEndDate = includeBorderEndDate;
	}
	public void setIncludeBorders(Boolean includeBorders) {
		this.includeBorders = includeBorders;
	}
}
